{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_performance_indicator:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Performance Indicator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is fundamental for any algorithm to measure the performance. In a multi-objective scenario, we can not calculate the distance to the true global optimum but must consider a set of solutions. Moreover, sometimes the optimum is not even known, and other techniques must be used. \n",
    "\n",
    "First, let us consider a scenario where the Pareto-front is known:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from pymoo.problems import get_problem\n",
    "from pymoo.visualization.scatter import Scatter\n",
    "\n",
    "# The pareto front of a scaled zdt1 problem\n",
    "pf = get_problem(\"zdt1\").pareto_front()\n",
    "\n",
    "# The result found by an algorithm\n",
    "A = pf[::10] * 1.1\n",
    "\n",
    "# plot the result\n",
    "Scatter(legend=True).add(pf, label=\"Pareto-front\").add(A, label=\"Result\").show()"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_gd:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Generational Distance (GD)\n",
    "\n",
    "The GD performance indicator <cite data-cite=\"gd\"></cite> measure the distance from solution to the Pareto-front. Let us assume the points found by our algorithm are the objective vector set $A=\\{a_1, a_2, \\ldots, a_{|A|}\\}$ and the reference points set (Pareto-front) is $Z=\\{z_1, z_2, \\ldots, z_{|Z|}\\}$. Then, \n",
    "\n",
    "\\begin{align}\n",
    "\\begin{split}\n",
    "\\text{GD}(A) & = & \\; \\frac{1}{|A|} \\; \\bigg( \\sum_{i=1}^{|A|} d_i^p \\bigg)^{1/p}\\\\[2mm]\n",
    "\\end{split}\n",
    "\\end{align}\n",
    "\n",
    "where $d_i$ represents the Euclidean distance (p=2) from $a_i$ to its nearest reference point in $Z$. Basically, this results in the average distance from any point $A$ to the closest point in the Pareto-front."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.indicators.gd import GD\n",
    "\n",
    "ind = GD(pf)\n",
    "print(\"GD\", ind(A))"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_gd_plus:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Generational Distance Plus (GD+)\n",
    "\n",
    "Ishibushi et. al. proposed in <cite data-cite=\"igd_plus\"></cite> GD+:\n",
    "\n",
    "\\begin{align}\n",
    "\\begin{split}\n",
    "\\text{GD}^+(A) & = & \\; \\frac{1}{|A|} \\; \\bigg( \\sum_{i=1}^{|A|} {d_i^{+}}^2 \\bigg)^{1/2}\\\\[2mm]\n",
    "\\end{split}\n",
    "\\end{align}\n",
    "\n",
    "where for minimization $d_i^{+} = max \\{ a_i - z_i, 0\\}$ represents the modified distance from $a_i$ to its nearest reference point in $Z$ with the corresponding value $z_i$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.indicators.gd_plus import GDPlus\n",
    "\n",
    "ind = GDPlus(pf)\n",
    "print(\"GD+\", ind(A))"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_igd:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Inverted Generational Distance (IGD)\n",
    "\n",
    "The IGD performance indicator <cite data-cite=\"igd\"></cite> inverts the generational distance and measures the distance from any point in $Z$ to the closest point in $A$.\n",
    "\n",
    "\\begin{align}\n",
    "\\begin{split}\n",
    "\\text{IGD}(A) & = & \\; \\frac{1}{|Z|} \\; \\bigg( \\sum_{i=1}^{|Z|} \\hat{d_i}^p \\bigg)^{1/p}\\\\[2mm]\n",
    "\\end{split}\n",
    "\\end{align}\n",
    "\n",
    "where $\\hat{d_i}$ represents the euclidean distance (p=2) from $z_i$ to its nearest reference point in $A$.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.indicators.igd import IGD\n",
    "\n",
    "ind = IGD(pf)\n",
    "print(\"IGD\", ind(A))"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_igd_plus:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Inverted Generational Distance Plus (IGD+)\n",
    "\n",
    "In <cite data-cite=\"igd_plus\"></cite> Ishibushi et. al. proposed IGD+ which is weakly Pareto compliant wheres the original IGD is not.\n",
    "\n",
    "\\begin{align}\n",
    "\\begin{split}\n",
    "\\text{IGD}^{+}(A) & = & \\; \\frac{1}{|Z|} \\; \\bigg( \\sum_{i=1}^{|Z|} {d_i^{+}}^2 \\bigg)^{1/2}\\\\[2mm]\n",
    "\\end{split}\n",
    "\\end{align}\n",
    "\n",
    "where for minimization $d_i^{+} = max \\{ a_i - z_i, 0\\}$ represents the modified distance from $z_i$ to the closest solution in $A$ with the corresponding value $a_i$.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.indicators.igd_plus import IGDPlus\n",
    "\n",
    "ind = IGDPlus(pf)\n",
    "print(\"IGD+\", ind(A))"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_hv:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Hypervolume"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For all performance indicators showed so far, a target set needs to be known. For Hypervolume only a reference point needs to be provided. First, I would like to mention that we are using the Hypervolume implementation from [DEAP](https://deap.readthedocs.io/en/master/). It calculates the area/volume, which is dominated by the provided set of solutions with respect to a reference point."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-align: center;\">\n",
    "    <img src=\"https://github.com/anyoptimization/pymoo-data/blob/main/docs/images/hv.png?raw=true\" width=\"350\">\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This image is taken from <cite data-cite=\"hv\"></cite> and illustrates a two objective example where the area which is dominated by a set of points is shown in grey.\n",
    "Whereas for the other metrics, the goal was to minimize the distance to the Pareto-front, here, we desire to maximize the performance metric."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.indicators.hv import HV\n",
    "\n",
    "ref_point = np.array([1.2, 1.2])\n",
    "\n",
    "ind = HV(ref_point=ref_point)\n",
    "print(\"HV\", ind(A))"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
